﻿#region

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using SceneUnpacker.Schematrix;

#endregion

namespace SceneUnpacker.se.gno
{
    public class ArchiveCollection
    {
        private readonly Statistics _statistics = new Statistics( "Total" );
        private bool _cancel;
        private DirectoryInfo _destination;
        private int _maxDepth = 2;
        private DirectoryInfo _source;
        private string extraContent = string.Empty;

        public ArchiveCollection()
        {
            Archives = new List<Archive>();
            AppendSourceName = true;
            Dirty = ( DirtyHandler ) Delegate.Combine( Dirty, new DirtyHandler( ArchiveCollection_Dirty ) );
        }

        public bool AppendArchivePrefix { get; set; }

        public bool AppendSourceName { get; set; }

        public List<Archive> Archives { get; private set; }

        public int ArchivesCount { get; private set; }

        public DirectoryInfo Destination
        {
            get { return _destination; }
            set
            {
                if ( ((_destination == null) || (value == null)) ||
                     ((value != null) && !PathUtils.Equals( value, _destination )) )
                {
                    _destination = value;
                    if ( DestinationChanged != null )
                    {
                        DestinationChanged( this, _destination );
                    }
                }
            }
        }

        public string ExtraContent
        {
            get { return extraContent; }
            set { extraContent = value ?? string.Empty; }
        }

        public int MaxDepth
        {
            get { return _maxDepth; }
            set
            {
                _maxDepth = value;
                if ( Dirty != null )
                {
                    Dirty( this );
                }
            }
        }

        internal string Name
        {
            get { return Source.Name; }
            set { throw new NotImplementedException(); }
        }

        public DirectoryInfo Source
        {
            get { return _source; }
            set
            {
                if ( ((_source == null) || (value == null)) || ((value != null) && !PathUtils.Equals( value, _source )) )
                {
                    _source = value;
                    Dirty( this );
                }
            }
        }

        public Statistics Statistics
        {
            get { return _statistics; }
        }

        public event ArchiveFoundHandler ArchiveFound;

        public event ArchiveProcessedHandler ArchiveProcessed;

        public event ContentChangedHandler ContentChanged;

        public event DestinationChangedHandler DestinationChanged;

        private event DirtyHandler Dirty;

        public event EnteringDirectoryHandler EnteringDirectory;

        public event ExtraContentErrorHandler ExtraContentError;

        public event LeavingDirectoryHandler LeavingDirectory;

        public event ProcessEndedHandler ProcessEnded;

        public event NewArchiveHandler ProcessNextArchive;

        public event ProcessStartedHandler ProcessStarted;

        private void ArchiveCollection_Dirty( object sender )
        {
            _cancel = true;            
            ContentChanged( this );
            FindArchives();
        }

        public void AsyncProcess( List<Archive> selected )
        {
            ThreadPool.QueueUserWorkItem( delegate { Process( selected ); } );
        }

        private void FindArchives()
        {
            Archives.Clear();
            if ( (_source != null) && _source.Exists )
            {
                _cancel = false;
                var pat = new Regex( @"^.*\.rar$|^*.001", RegexOptions.IgnoreCase );
                FindArchivies( _source, pat, _maxDepth );
            }
        }

        private void FindArchivies( DirectoryInfo d, Regex pat, int maxDepth )
        {
            if ( EnteringDirectory != null )
            {
                EnteringDirectory( this, d );
            }
            try
            {
                var list = new List<Archive>();
                FileInfo[] files = d.GetFiles();
                Array.Sort( files, delegate( FileInfo f1, FileInfo f2 ) { return f1.Name.CompareTo( f2.Name ); } );
                foreach ( var info in files )
                {
                    if ( pat.IsMatch( info.Name ) )
                    {
                        bool flag = true;
                        foreach ( var archive in list )
                        {
                            if ( archive.Contains( info ) )
                            {
                                flag = false;
                                break;
                            }
                        }
                        if ( flag )
                        {
                            var item = new Archive( _source, info );
                            item.Unrar.ExtractionProgress += Unrar_ExtractionProgress;
                            list.Add( item );
                            Archives.Add( item );
                            ArchiveFound( this, item );
                        }
                    }
                }
                if ( --maxDepth >= 0 )
                {
                    foreach ( var info2 in d.GetDirectories() )
                    {
                        if ( _cancel )
                        {
                            goto Label_0142;
                        }
                        FindArchivies( info2, pat, maxDepth );
                    }
                }
            }
            catch
            {
            }
            Label_0142:
            if ( LeavingDirectory != null )
            {
                LeavingDirectory( this, d );
            }
        }

        private string GetArchivePrefix( Archive a )
        {
            if ( a.File.Directory.FullName.Length <= Source.FullName.Length )
            {
                return "";
            }
            return a.File.Directory.FullName.Substring( Source.FullName.Length + 1 );
        }

        public void Process( List<Archive> selected )
        {
            if ( Destination == null )
            {
                throw new NullReferenceException( "No destination set!" );
            }
            ArchivesCount = selected.Count;
            if ( ProcessStarted != null )
            {
                Statistics.Begin();
            }
            ProcessStarted( this );
            try
            {
                foreach ( var archive in Archives )
                {
                    if ( selected.Contains( archive ) && !_cancel )
                    {
                        Statistics.InitSplit( archive.ToString() );
                        if ( ProcessNextArchive != null )
                        {
                            ProcessNextArchive( this, archive );
                        }
                        try
                        {
                            DirectoryInfo dest = _destination;
                            if ( AppendSourceName )
                            {
                                dest = new DirectoryInfo( Path.Combine( dest.FullName, _source.Name ) );
                            }
                            if ( AppendArchivePrefix )
                            {
                                dest = new DirectoryInfo( Path.Combine( dest.FullName, GetArchivePrefix( archive ) ) );
                            }
                            if ( !dest.Exists )
                            {
                                dest.Create();
                            }
                            if ( ExtraContent != string.Empty )
                            {
                                foreach ( var str in ExtraContent.Split( new[] {' '} ) )
                                {
                                    foreach ( var info2 in archive.Directory.GetFiles( str ) )
                                    {
                                        string path = Path.Combine( dest.FullName, info2.Name );
                                        if ( File.Exists( path ) )
                                        {
                                            goto Label_01AC;
                                        }
                                        Label_0167:
                                        try
                                        {
                                            info2.CopyTo( path, true );
                                        }
                                        catch ( Exception exception )
                                        {
                                            bool flag = true;
                                            if ( ExtraContentError != null )
                                            {
                                                var e = new ExtraContentErrorEventArgs( exception );
                                                ExtraContentError( this, e );
                                                flag = !e.ContinueOperation;
                                            }
                                            if ( !flag )
                                            {
                                                goto Label_0167;
                                            }
                                        }
                                        Label_01AC:
                                        ;
                                    }
                                }
                            }
                            archive.unpack( dest );
                            continue;
                        }
                        finally
                        {
                            if ( ArchiveProcessed != null )
                            {
                                ArchiveProcessed( this, archive );
                            }
                        }
                    }
                }
            }
            finally
            {
                if ( ProcessEnded != null )
                {
                    ProcessEnded( this );
                }
            }
        }

        internal void Stop()
        {
            _cancel = true;
        }

        private void Unrar_ExtractionProgress( object sender, ExtractionProgressEventArgs e )
        {
            Statistics.AddSample( e );
        }

        #region Nested type: DirtyHandler

        private delegate void DirtyHandler( object sender );

        #endregion
    }
}